package org.tea.springdata.mina;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;

import javax.crypto.Cipher;

/**
 * RSA公钥/密钥生成工具
 * 
 * @author John zhang
 * @version 0.1
 */
public class RSAUtils {

	/**
	 * 十六进制字符集
	 */
	private static char[] HEXCHAR = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
	/**
	 * rsa密钥位数
	 */
	private static int KEY_LENGTH = 512;
	/**
	 * rsa最大加密密文大小
	 */
	private static int MAX_ENCRYPT_BLOCK = 128;
	/**
	 * rsa最大解密密文大小
	 */
	private static final int MAX_DECRYPT_BLOCK = 128;

	public static void main(String args[]) {
		RSAUtils rsaUtils = new RSAUtils();
		try {
			KeyPair clientKeyPair = rsaUtils.generateKey();
			RSAPublicKey publicKey = (RSAPublicKey) clientKeyPair.getPublic();
			RSAPrivateKey privateKey = (RSAPrivateKey) clientKeyPair.getPrivate();
			String clientHexString = toHexString(publicKey.getEncoded());
			System.out.println("client encoded public key is 		: " + toHexString(publicKey.getEncoded()) + "\n length : " + clientHexString.length());
			// System.out.println("client private key encoded is : " +
			// toHexString(privateKey.getEncoded()).length());
			RSAPublicKeySpec keySpec = new RSAPublicKeySpec(publicKey.getModulus(), publicKey.getPublicExponent());
			// X509EncodedKeySpec keySpec = new
			// X509EncodedKeySpec(publicKey.getEncoded());
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			RSAPublicKey serverPublickey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
			String serverHexString = toHexString1(serverPublickey.getEncoded());
			System.out.println("server decoded client public key is : " + serverHexString + "\n length : " + serverHexString.length());
			// System.out.println("server decoded client public key octal value is : " + toOctalString( serverPublickey.getEncoded() ));
			// System.out.println("server decoded client public key binary value is : " + toBinaryString( serverPublickey.getEncoded() ));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public KeyPair generateKey() throws NoSuchAlgorithmException {
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
		keyPairGen.initialize(KEY_LENGTH, new SecureRandom());

		KeyPair keyPair = keyPairGen.generateKeyPair();
		return keyPair;
	}

	/**
	 * 利用公钥将数据进行RSA加密并返回
	 * 
	 * @return byte[]
	 */
	public byte[] encrypt(RSAPublicKey publicKey, byte[] data) {
		if (publicKey != null) {
			ByteArrayOutputStream out = null;
			try {
				Cipher cipher = Cipher.getInstance("RSA");
				cipher.init(Cipher.ENCRYPT_MODE, publicKey);
				out = new ByteArrayOutputStream();
				int length = data.length;
				byte[] buf = new byte[MAX_ENCRYPT_BLOCK];
				// 对数据分段加密
				for (int i = 0, offset = 0; (offset = i * MAX_ENCRYPT_BLOCK) < length; i++) {
					if (length > MAX_ENCRYPT_BLOCK + offset) {
						buf = cipher.doFinal(data, offset, MAX_ENCRYPT_BLOCK);
					} else {
						buf = cipher.doFinal(data, offset, length - offset);
					}
					out.write(buf, 0, buf.length);
				}
				return out.toByteArray();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					if (out != null)
						out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	/**
	 * 利用私钥将加密后的数据进行解密
	 * 
	 * @return byte[]
	 */
	public byte[] decrypt(RSAPrivateKey privateKey, byte[] data) {
		if (privateKey != null) {
			ByteArrayOutputStream out = null;
			try {
				Cipher cipher = Cipher.getInstance("RSA");
				cipher.init(Cipher.DECRYPT_MODE, privateKey);
				out = new ByteArrayOutputStream();
				int length = data.length;
				byte[] buf = new byte[MAX_DECRYPT_BLOCK];
				// 对数据分段解密
				for (int i = 0, offset = 0; (offset = i * MAX_DECRYPT_BLOCK) < length; i++) {
					if (length > MAX_DECRYPT_BLOCK + offset) {
						buf = cipher.doFinal(data, offset, MAX_DECRYPT_BLOCK);
					} else {
						buf = cipher.doFinal(data, offset, length - offset);
					}
					out.write(buf, 0, buf.length);
				}
				return out.toByteArray();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					if (out != null)
						out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	public static String toHexString(byte[] b) {
		StringBuilder sb = new StringBuilder(b.length * 2);
		for (int i = 0; i < b.length; i++) {
			sb.append(HEXCHAR[(b[i] & 0xf0) >>> 4]);
			sb.append(HEXCHAR[b[i] & 0x0f]);
		}
		return sb.toString();
	}

	public static String toHexString1(byte[] b) {
		StringBuilder sb = new StringBuilder(b.length * 2);
		for (int i = 0; i < b.length; i++) {
			String val = Integer.toHexString( b[i] & 0xff );
			if (val.length() == 1) {
				sb.append("0");
			}
			sb.append(val);
		}
		return sb.toString();
	}
	
	public static String toOctalString(byte[] b) {
		StringBuilder sb = new StringBuilder(b.length * 3);
		for (int i = 0; i < b.length; i++) {
			String val = Integer.toOctalString( b[i] & 0xff );
			sb.append(val);
		}
		return sb.toString();
	}
	
	public static String toBinaryString(byte[] b) {
		StringBuilder sb = new StringBuilder(b.length * 8);
		for (int i = 0; i < b.length; i++) {
			String val = Integer.toBinaryString( b[i] & 0xff );
			sb.append(val);
		}
		return sb.toString();
	}
	
	public static byte[] hexToBytes(String hex) {
		byte[] b = new byte[ hex.length() / 2 ];
		for(int i = 0; i < b.length; i++ ) {
			b[i] = (byte) Integer.parseInt(hex.substring(i * 2 + 1, i * 2 + 2), 16);
		}
		return b;
	}
}